# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: chensili

from coin.proto.coin_request_pb2 import AccountRequestProto
from coin.proto.coin_market_query_pb2 import ExchangeTicker
from coin.proto.coin_query_pb2 import (AccountBalance, CurrencyBalance, EstimateValue)
from xunkemgmt_client.support.accounting.logic.query_util import calculate_exchange_rate


def _get_least_tickers(acct_req: AccountRequestProto,
                       exchange_tickers: [ExchangeTicker]):
  candidates = ['ExchangeRates', 'Gdax', 'Kraken'] + [acct_req.exchange]
  exchange_tickers = [exchange_ticker for exchange_ticker in exchange_tickers
                      if exchange_ticker.exchange in candidates]
  return exchange_tickers


def get_ticker_price(currency, accounting_currency, ticker):
  price = None

  # equal
  if price is None and currency == accounting_currency:
    price = 1.0

  # spot
  ticker_symbol = currency + '-' + accounting_currency
  if price is None and ticker_symbol in ticker:
    price = ticker[ticker_symbol]

  # reverse spot
  ticker_symbol = accounting_currency + '-' + currency
  if price is None and ticker_symbol in ticker:
    price = 1.0 / ticker[ticker_symbol]

  # futures
  ticker_symbol = currency + '-' + accounting_currency + ".PERPETUAL"
  if price is None and ticker_symbol in ticker:
    price = ticker[ticker_symbol]

  # reverse futures
  ticker_symbol = accounting_currency + '-' + currency + ".PERPETUAL"
  if price is None and ticker_symbol in ticker:
    price = 1.0 / ticker[ticker_symbol]

  return price


def get_ticker_price_from_cross_rate(currency, accounting_currency, ticker, cross_price_quote_list):
  price = None

  if cross_price_quote_list:
    for cross_price_quote in cross_price_quote_list:
      cross_price1 = get_ticker_price(currency, cross_price_quote, ticker)
      cross_price2 = get_ticker_price(cross_price_quote, accounting_currency, ticker)
      if cross_price1 is not None and cross_price2 is not None:
        price = cross_price1 * cross_price2
        break
  return price


def calculate_asset_value(account_request: AccountRequestProto,
                          currency_balance: CurrencyBalance,
                          exchange_tickers: [ExchangeTicker],
                          accounting_currency: str) -> EstimateValue:
  assert isinstance(currency_balance, CurrencyBalance), currency_balance
  assert isinstance(exchange_tickers, list)
  
  expiry_thold_sec = 1800
  total_value = None
  total_value_in_usd = None
  assert accounting_currency and accounting_currency != "UNKNOWN", \
      'Accounting currency unavailable.'

  if currency_balance.currency == accounting_currency:
    total_value = currency_balance.total
  else:
    acct_req = AccountRequestProto(
        market_type=account_request.market_type,
        exchange=account_request.exchange)
    least_exchange_tickers = _get_least_tickers(acct_req, exchange_tickers)
    # due to the fact that same asset is listed in multi exchanges withb different
    # price. calculate asset price with the least exchange tickers first.
    # if not found, try all tickers as well
    price = calculate_exchange_rate(
        [currency_balance.currency], accounting_currency, least_exchange_tickers,
        expiry_thold_sec=expiry_thold_sec)[0]
    if price is None:
      price = calculate_exchange_rate(
        [currency_balance.currency], accounting_currency, exchange_tickers,
        expiry_thold_sec=expiry_thold_sec)[0]
    if price is not None:
      total_value = price * currency_balance.total
    else:
      pass
  accounting_currency_in_usd = calculate_exchange_rate(
      [accounting_currency], 'USD', exchange_tickers,
      expiry_thold_sec=expiry_thold_sec)[0]
  if total_value is not None and accounting_currency_in_usd is not None:
    total_value_in_usd = total_value * accounting_currency_in_usd
  return EstimateValue(total_value=total_value,
                       total_value_in_usd=total_value_in_usd,)


def estimate_value(account_balance: AccountBalance,
                   tickers,
                   accounting_currency,
                   cross_price_quote_list=None):
  estimate_balance = AccountBalance()
  estimate_balance.CopyFrom(account_balance)
  estimate_balance.ClearField('each_balance')
  for balance in account_balance.each_balance:
    if balance.currency == accounting_currency:
      estimate_balance.each_balance.add().CopyFrom(balance)
    else:
      price = get_ticker_price(balance.currency, accounting_currency, tickers)
      if price is None:
        price = get_ticker_price_from_cross_rate(balance.currency,
                                                 accounting_currency,
                                                 tickers,
                                                 cross_price_quote_list)
      if price is not None:
        each_estimate_balance = estimate_balance.each_balance.add()
        each_estimate_balance.CopyFrom(balance)
        each_estimate_balance.total *= price
        each_estimate_balance.available *= price
        each_estimate_balance.hold *= price
      else:
        pass
  return estimate_balance


def gen_estimate_value_proto(quote_value, coin_value, total_value,
                             accounting_currency=None, symbol=None):
  estimate = EstimateValue(quote_value=quote_value,
                           coin_value=coin_value,
                           total_value=total_value)
  if accounting_currency is not None:
    estimate.accounting_currency = accounting_currency
  if symbol is not None:
    estimate.symbol = symbol
  return estimate
